﻿using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using SqlSugar;
using DbType = SqlSugar.DbType;

namespace 数据迁移工具.Common.DbContext
{
    public class SugarDbContext : IDisposable
    {
        public static List<ConnectionConfig> ConnectionConfigs { get; set; }
        protected Dictionary<string, int> tableRowMap = new Dictionary<string, int>();

        public SqlSugarClient DB { get; }
        public string ConfigId
        {
            get
            {
                return DB.CurrentConnectionConfig.ConfigId.ToString();
            }
        }

        public SugarDbContext()
        {
            InitCheck();
            DB = new SqlSugarClient(ConnectionConfigs);
        }

        public SugarDbContext(ConnectionConfig config)
        {
            DB = new SqlSugarClient(config);
            OpenAopLog();
        }

        public void OpenAopLog()
        {
            DB.Aop.OnLogExecuting = (sql, parameters) =>
            {
                ConsoleLog.Info("RichText",$"SQL 执行：{sql}");
            };

            DB.Aop.OnError = (exp) =>
            {
                ConsoleLog.Error("RichText",$"SQL 执行出错：{exp.Message}");
            };
        }

        private void InitCheck()
        {
            if (ConnectionConfigs == null || ConnectionConfigs.Count == 0)
                throw new Exception("请先调用 SugarDbContext.ConnectionConfigs 为连接配置文件赋值");
        }

        /// <summary>
        /// 获取源表信息
        /// </summary>
        /// <param name="dbName"></param>
        /// <returns></returns>
        public virtual List<TableInfo> GetTableInfos(string dbName) { return null; }

        #region 实体类生成

        /// <summary>
        /// 根据表结构生成实体类
        /// </summary>
        /// <param name="configId"></param>
        /// <param name="namesp"></param>
        public virtual void GenerateModels(string configId, string modelPath, List<string> tables, string ignorePrefix = "", string namesp = "Default.Models", Action<int> processMonitor = null, Action<string> processLog = null)
        {
            if (!Directory.Exists(modelPath))
                Directory.CreateDirectory(modelPath);
            int total = tables.Count;
            int current = 0;
            DB.GetConnection(configId)
                .DbFirst
                .IsCreateAttribute()
                .FormatFileName(arg1 =>
                {
                    current++;
                    if (processMonitor != null)
                    {
                        double res = Math.Round(1.0 * current / total, 2) * 100;
                        processMonitor.Invoke((int)res);
                    }
                    if (!string.IsNullOrEmpty(ignorePrefix))
                    {
                        arg1 = arg1.Replace(ignorePrefix, "");
                    }
                    return arg1;
                })
                .FormatClassName(arg1 =>
                {
                    if (!string.IsNullOrEmpty(ignorePrefix))
                    {
                        arg1 = arg1.Replace(ignorePrefix, "");
                    }
                    return char.ToUpper(arg1[0]) + arg1.Substring(1);
                })
                .SettingNamespaceTemplate(arg1 =>
                {
                    return arg1 += "using System.ComponentModel.DataAnnotations;\n";
                })
                .SettingPropertyTemplate((column, arg1, arg2) =>
                {
                    // 初始化用于拼接的字符串
                    StringBuilder builder = new StringBuilder("\n");
                    string propertyStr = $"\t\t   public {arg2} {ConvertToPascalCase(column.DbColumnName)} {{get; set;}}";

                    int len = column.Length;
                    if (arg2.Contains("DateTime", StringComparison.OrdinalIgnoreCase))
                        len = 0;
                    // 如果字段是主键，则添加 [SugarColumn(IsPrimaryKey=true)] 特性
                    if (column.IsPrimarykey)
                    {
                        builder.AppendLine($"\t\t   [SugarColumn(IsPrimaryKey=true,ColumnName =\"{column.DbColumnName}\",ColumnDescription = \"{column.ColumnDescription}\",Length = {len})]");
                    }
                    else
                    {
                        builder.AppendLine($"\t\t   [SugarColumn(ColumnName =\"{column.DbColumnName}\",ColumnDescription = \"{column.ColumnDescription}\",Length = {len})]");
                    }

                    // 如果字段可以为空，字段类型后面加？否则则添加 [Required] 特性
                    if (!column.IsNullable)
                    {
                        builder.AppendLine("\t\t   [Required]");
                    }

                    // 拼接属性
                    builder.AppendLine(propertyStr);
                    string logInfo = $"正在生成表： {column.TableName} 对应的实体类属性：{arg2} {column.DbColumnName} ...";

                    processLog?.Invoke(logInfo);

                    // 返回处理后的字段属性字符串
                    return builder.ToString();
                })
                .Where(n => Array.IndexOf(tables.ToArray(), n) != -1)
                .CreateClassFile(modelPath, namesp);
        }

        /// <summary>
        /// 生成临时 Model 实体类
        /// </summary>
        /// <param name="configId"></param>
        /// <param name="modelPath"></param>
        /// <param name="tables"></param>
        /// <param name="ignorePrefix"></param>
        /// <param name="namesp"></param>
        /// <param name="processMonitor"></param>
        /// <param name="processLog"></param>
        public virtual void GenerateTmpModels(string configId, string modelPath, List<string> tables, string ignorePrefix = "", string namesp = "Default.Models", Action<string> processLog = null)
        {
            if (!Directory.Exists(modelPath))
                Directory.CreateDirectory(modelPath);
            int total = tables.Count;
            DB.GetConnection(configId)
                .DbFirst
                .IsCreateAttribute()
                .SettingNamespaceTemplate(arg1 =>
                {
                    return arg1 += "using System.ComponentModel.DataAnnotations;\n";
                })
                .SettingPropertyTemplate((column, arg1, arg2) =>
                {
                    // 初始化用于拼接的字符串
                    StringBuilder builder = new StringBuilder("\n");
                    string propertyStr = $"\t\t   public {arg2} {ConvertToPascalCase(column.DbColumnName)} {{get; set;}}";

                    int len = column.Length;
                    if (arg2.Contains("DateTime", StringComparison.OrdinalIgnoreCase))
                        len = 0;
                    // 如果字段是主键，则添加 [SugarColumn(IsPrimaryKey=true)] 特性
                    if (column.IsPrimarykey)
                    {
                        builder.AppendLine($"\t\t   [SugarColumn(IsPrimaryKey=true,ColumnName =\"{column.DbColumnName}\",ColumnDescription = \"{column.ColumnDescription}\",Length = {len})]");
                    }
                    else
                    {
                        builder.AppendLine($"\t\t   [SugarColumn(ColumnName =\"{column.DbColumnName}\",ColumnDescription = \"{column.ColumnDescription}\",Length = {len})]");
                    }

                    // 如果字段可以为空，字段类型后面加？否则则添加 [Required] 特性
                    if (!column.IsNullable)
                    {
                        builder.AppendLine("\t\t   [Required]");
                    }

                    // 拼接属性
                    builder.AppendLine(propertyStr);
                    string logInfo = $"正在生成表： {column.TableName} 对应的实体类属性：{arg2} {column.DbColumnName} ...";
                    processLog?.Invoke(logInfo);

                    // 返回处理后的字段属性字符串
                    return builder.ToString();
                })
                .Where(n => Array.IndexOf(tables.ToArray(), n) != -1)
                .CreateClassFile(modelPath, namesp);
        }
        #endregion

        #region 生成数据库表结构

        /// <summary>
        /// 根据实体类生成数据表
        /// </summary>
        /// <param name="configId"></param>
        public virtual void GenerateTables(string configId)
        {
            if (DynamicHelper.ModelAssembly == null)
            {
                ConsoleLog.Error("RichText", "数据源对应的实体类程序集并未生成");
                return;
            }
            List<Type> types = DynamicHelper.ModelAssembly.GetTypes().Where(s => s.GetCustomAttribute<SugarTable>() != null).ToList();
            ConnectionConfig? connConfig = ConnectionConfigs.FirstOrDefault(c => c.ConfigId.ToString() == configId);
            if (connConfig == null)
                throw new Exception("指定的数据源Id未找到");
            SqlSugarClient sourceContext = new SqlSugarClient(connConfig, db =>
            {
                db.Aop.OnLogExecuting = (sql, parameters) =>
                {
                    ConsoleLog.Info("RichText",$"SQL 执行完成：{sql}");
                };

                db.Aop.OnError = (exp) =>
                {
                    ConsoleLog.Error("RichText", $"SQL 执行出错：{exp.Message}");
                };
            });

            sourceContext
                .CodeFirst
                .InitTablesWithAttr(types.ToArray());
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 将字符串转换为帕斯卡命名法
        /// </summary>
        /// <param name="name">需要变动的名称：user_id => UserId</param>
        /// <returns></returns>
        protected string ConvertToPascalCase(string name)
        {
            if (name.Contains("_"))
                name = string.Concat(name.Split('_').Select(word => char.ToUpper(word[0]) + word.Substring(1).ToLower()));
            else
                name = char.ToUpper(name[0]) + name.Substring(1);
            return name;
        }

        /// <summary>
        /// 执行从数据源向目标源的数据迁移
        /// </summary>
        /// <param name="statusStrip"></param>
        /// <param name="sourceId"></param>
        /// <param name="targetId"></param>
        /// <param name="sugarType"></param>
        /// <param name="table"></param>
        /// <param name="entityType"></param>
        /// <returns></returns>
        protected int QueryAndInsert(string sourceId, string targetId, Type sugarType, string table, Type entityType)
        {
            ConnectionConfig? connConfig = ConnectionConfigs.FirstOrDefault(c => c.ConfigId == sourceId);
            if (connConfig == null)
                throw new Exception("指定的数据源Id未找到");
            SqlSugarClient sourceContext = new SqlSugarClient(connConfig);

            ConnectionConfig? dstConfig = ConnectionConfigs.FirstOrDefault(c => c.ConfigId == targetId);
            if (dstConfig == null)
                throw new Exception("指定的目标源Id未找到");
            SqlSugarClient targetContext = new SqlSugarClient(dstConfig, db =>
            {
                db.Aop.OnError = (exp) =>
                {
                    ConsoleLog.Error("statusStrip",$"SQL 执行出错：{exp.Message}");
                };
            });

            // 起始位置，偏移量
            int beginAddr = 1, offset = 10000;
            int totalRow = 0;
            int dstPosition = tableRowMap[table];
            offset = Math.Min(offset, dstPosition);
            if (offset == 0)
                return 0;

            var queryableMethod = sugarType.GetMethod("Queryable", new[] { typeof(string) }).MakeGenericMethod(entityType);
            var queryableInstance = queryableMethod.Invoke(sourceContext, new string[] { table });
            var toPageListMethod = queryableInstance.GetType().GetMethod("ToPageList", new[] { typeof(int), typeof(int) });

            //var insertMethod = sugarType.GetMethod("Insertable", new[] { typeof(List<>) }).MakeGenericMethod(entityType);
            //var insertableInstance = insertMethod.Invoke(targetContext, new object[] { list });
            //var executeMethod = insertableInstance.GetType().GetMethod("ExecuteCommand");
   
            var fastestMethod = sugarType.GetMethod("Fastest").MakeGenericMethod(entityType);
            var fastestInstance = fastestMethod.Invoke(targetContext, null);
            Type listType = typeof(List<>).MakeGenericType(entityType);
            var bulkCopyMethod = fastestInstance.GetType().GetMethod("BulkCopy", new[] { listType });

            while (totalRow < dstPosition)
            {
                try
                {
                    targetContext.BeginTran();
                    var list = toPageListMethod.Invoke(queryableInstance, new object[] { beginAddr, offset });
                    var result = bulkCopyMethod.Invoke(fastestInstance, new object[] { list });
                    //int rows = (int)executeMethod.Invoke(insertableInstance, null);

                    totalRow = (totalRow + offset) > dstPosition ? dstPosition : (totalRow + offset);
                    double percent = Math.Round(totalRow * 1.0 / dstPosition, 4) * 100;
                    string percentMsg = $"表 {table} 数据迁移进度： {percent:F2} %";
                    ConsoleLog.Info("richText",percentMsg);
                    StatusStrip statusStrip = (StatusStrip)ConsoleLog.GetControl("statusStrip");
                    statusStrip.Invoke(() =>
                    {
                        statusStrip.Items["CurTbNameLb"].Text = table;
                        ToolStripProgressBar progressBar = (ToolStripProgressBar)statusStrip.Items["Probar"];
                        progressBar.Value = (int)percent;
                        statusStrip.Items["ProLb"].Text = $"{percent:F2} %";
                    });
                    beginAddr++;
                    targetContext.CommitTran();
                }
                catch (Exception ex)
                {
                    targetContext.RollbackTran();
                    ConsoleLog.Error("richText",ex.ToString() + ex.StackTrace);
                }
            }
            return totalRow;
        }
        #endregion

        #region 视图处理
        /// <summary>
        /// 不同类型的数据库视图格式转换
        /// </summary>
        /// <param name="targetId">目标数据库Id</param>
        /// <param name="srcDbName">源库名</param>
        /// <param name="dstDbName">目标库名</param>
        public virtual void ViewConvert(string srcMode, DbType dstType,string targetId,string dstDbName) { }

        /// <summary>
        /// 获取视图信息
        /// </summary>
        /// <param name="mode"></param>
        /// <returns></returns>
        protected virtual List<ViewDefinition> GetViewDefinitions(string mode) { return null; }

        /// <summary>
        /// 在目标数据库创建视图
        /// </summary>
        /// <param name="dstContext"></param>
        /// <param name="viewName"></param>
        /// <param name="createViewSql"></param>
        /// <returns></returns>
        public bool CreateView(SugarDbContext dstContext, string viewName, string createViewSql)
        {
            bool flag = false;
            try
            {
                dstContext.DB.Ado.ExecuteCommand(createViewSql);
                flag = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return flag;
        }
        #endregion

        #region 数据迁移
        public virtual void DbMigrate(DbType targetType,string targetId, string dbName, string namesp = "Default.Models") { }
        #endregion

        public void Dispose()
        {
            DB?.Close();
            DB?.Dispose();
        }
    }
}
